package jobs

import (
	"bytes"
	"fmt"
	"gin_template/core/email"
	"gin_template/core/setting"
	"gin_template/core/snowflake"
	"gin_template/models"
	"gin_template/services/dataService/tianxing"
	"html/template"
	"strconv"
	"time"

	"sync"

	"go.uber.org/zap"
)

//var CronJobService = cron.New() // 新建一个定时任务对象
var lock sync.Mutex
var weatherEmalChan = make(chan *models.User, 10)
var subscribSuccChan = make(chan *models.User, 10)
var subscribRepeatChan = make(chan *models.User, 10)
var appConf = setting.AppConf

type weatherEmailNotify struct {
	Title      string
	UserName   string
	NotifyData interface{}
}
type tips struct {
	Weather string
	Love    string
	Star    string
	Holiday string
}

/*
	邮件订阅：
		1 用户发送邮件订阅邮件
		2 定时任务遍历收件箱查询是否存在订阅邮件，若有则存入数据库持久化并推送到邮件服务发送
		订阅成功邮件（需要检查是否已订阅）
		3 定时任务每天早上七点二十查询存量已订阅用户推送天气邮件
		4 开启独立邮件推送服务，监听对应通道，发送不同的邮件
*/

func RunWeatherTips() {
	zap.L().Info("Weather Job Starting...")
	// 注册邮件服务用于邮件推送
	processEmailSendTasks()
	//processLoadSubscriber()

	// 每5s执行新订阅用户的数据存入，并将订阅用户推入邮件服务
	CronJobService.AddFunc("*/5 * * * * ?", processLoadSubscriber)
	// 每天七点二十开始执行存量订阅用户的推送
	CronJobService.AddFunc("0 20 7 * * ?", processSubscriberPush)

	//CronJobService.Start()
	////延迟关闭任务
	//defer CronJobService.Stop()
	//select {}
}

// processEmailSendTasks 注册邮件服务用于邮件的推送
func processEmailSendTasks() {
	//启动一个goroutine用于邮件发送
	go func() {
		zap.L().Info(" Email server is running ")
		select {
		case user := <-weatherEmalChan:
			//发送天气提醒邮件
			processSendWeatherEmail(user)
		case user := <-subscribSuccChan:
			//发送订阅成功邮件
			processCommonEmail("订阅成功提醒", "您已成功订阅天气邮件服务", user)
		case user := <-subscribRepeatChan:
			//发送请勿重复订阅邮件
			processCommonEmail("订阅失败提醒", "请勿重复发起订阅请求", user)
		}
	}()
}

// processSubscriberPush 定时任务将存量订阅用户推送进邮件服务
func processSubscriberPush() {
	users := models.QuerySubscribeUser()
	for _, user := range users {
		subscribSuccChan <- user
	}
	zap.L().Info("存量订阅用户数据推送成功")
}

// processLoadSubscriber 定时任务加载订阅用户并存入数据库
func processLoadSubscriber() {
	var emailServerConfig = &setting.AppConf.EmailConfig
	users, err := email.GetSubscribeUser(emailServerConfig.RecvHost+":"+emailServerConfig.RecvPort, emailServerConfig.Sender.Account, emailServerConfig.Sender.Password)
	if err != nil {
		zap.L().Error("GetSubscribeUser err", zap.Error(err))
	}
	var subscriber []*models.User
	for _, user := range users {
		userId := strconv.FormatInt(snowflake.GenId(), 10)
		subscriber = append(subscriber, &models.User{
			UserId:   userId,
			Name:     user.Name,
			Email:    user.Address,
			Password: "",
			Age:      -1,
			Status:   1,
		})
	}
	//如果存在订阅用户，则将其存入数据库user表
	if len(subscriber) > 0 {
		// 检查是否存在已订阅用户，若是，则提示：请勿重复订阅
		users := models.QuerySubscribeUser()
		//fmt.Println("====users=====", users)
		// 对users和subscriber进行处理
		// 新用户插入数据库，返回订阅成功
		// 已订阅用户，返回：请勿重复订阅
		var newer []*models.User
		var older []*models.User
		// 两个列表中重复的部分，即为已订阅用户
		// 两个列表中不重复的部分，即为新订阅用户
		for _, s := range subscriber {
			if ok := find(users, func(u *models.User) bool {
				if u.Email == s.Email {
					return true
				}
				return false
			}); ok {
				older = append(older, s)
			} else {
				newer = append(newer, s)
			}
		}

		err := models.BatchInsert(newer)
		if err != nil {
			zap.L().Error("新订阅用户插入数据库失败", zap.Error(err))
		}
		zap.L().Info("新订阅用户数据获取成功")
		//将所有订阅成功者推送至订阅成功队列发送订阅成功邮件
		for _, s := range newer {
			subscribSuccChan <- s
		}
		// 重复订阅者推送者错误邮件队列
		for _, s := range older {
			subscribRepeatChan <- s
		}
	}

}

func filter(slice []*models.User, f func(u *models.User) bool) ([]*models.User, []*models.User) {
	var shot []*models.User
	var unShot []*models.User
	for _, item := range slice {
		if ok := f(item); ok {
			shot = append(shot, item)
		} else {
			unShot = append(unShot, item)
		}
	}
	return shot, unShot
}

func find(slice []*models.User, f func(u *models.User) bool) bool {
	for _, item := range slice {
		if ok := f(item); ok {
			return true
		}
	}
	return false
}

// processSendEmailTasks 获取天气数据并向所有订阅用户发送邮件
func processSendWeatherEmail(user *models.User) {
	//1 通过天气api获取数据推送穿衣建议
	weatherTips := getQWeatherTips()
	////2 获取每日情话
	loveTips := getDailyLoveTips()
	//3 星座运势
	star := getDailyStarTips()
	// 4 节假日
	holiday := getHolidayTips()
	//4 将数据渲染到html模板并发送邮件
	tip := tips{
		Weather: weatherTips,
		Love:    loveTips,
		Star:    star,
		Holiday: holiday,
	}
	sendEmail("每日天气提醒", "email.html", user, tip)
}

// processErrEmail 发送通用提示邮件
func processCommonEmail(subject string, tip string, user *models.User) {
	sendEmail(subject, "common.html", user, tip)
}
func getWeatherTips() string {
	weatherConf := &setting.AppConf.ApiConfig.Tianxing
	params := &tianxing.WeatherConf{
		Config: tianxing.Config{
			Key: weatherConf.Key,
			Url: weatherConf.Url,
		},
		City: "武汉市",
	}
	res := tianxing.RequestSayWeather(params)
	return res
}

func getQWeatherTips() string {
	weatherConf := &setting.AppConf.ApiConfig.Qweather
	params := &tianxing.Config{
		Key: weatherConf.Key,
		Url: weatherConf.Url,
	}
	location := "101200114"
	res := tianxing.RequestQweather(params, location)
	return res
}

func getDailyLoveTips() string {
	conf := &setting.AppConf.ApiConfig.Tianxing
	params := &tianxing.Config{
		Key: conf.Key,
		Url: conf.Url,
	}
	res := tianxing.RequestSayLove(params)
	return res
}

func getDailyStarTips() string {
	conf := &setting.AppConf.ApiConfig.Tianxing
	params := &tianxing.Config{
		Key: conf.Key,
		Url: conf.Url,
	}
	astro := "天蝎座"
	res := tianxing.RequestStar(params, astro)
	return res
}

func getHolidayTips() string {
	conf := &setting.AppConf.ApiConfig.Tianxing
	params := &tianxing.Config{
		Key: conf.Key,
		Url: conf.Url,
	}
	date := time.Now().String()[0:10]
	res := tianxing.RequestHoliday(params, date)
	return res
}

// 发送邮件提醒
func sendEmail(subject string, tempFile string, recipient *models.User, data interface{}) {
	zap.L().Info("sendEmail")
	// 获取邮件模板
	tmpl, err := template.ParseFiles("templates/" + tempFile)
	if err != nil {
		zap.L().Error("邮件模板路径解析失败", zap.Error(err))
		return
	}

	//加锁防止邮件模板被竞争，导致邮件发送失败
	lock.Lock()
	var tempData interface{}
	if subject == "每日天气提醒" {
		tempData = data.(tips)
	} else {
		tempData = data.(string)
	}
	zap.L().Info("开始发送邮件---" + recipient.Email)
	sendData := weatherEmailNotify{
		Title:      subject,
		UserName:   recipient.Name,
		NotifyData: tempData,
	}

	fmt.Println("=====", sendData)

	// 渲染模板内容
	var buff bytes.Buffer
	if err := tmpl.Execute(&buff, sendData); err != nil {
		zap.L().Error("邮件模板渲染失败", zap.Error(err))
		return
	}
	//fmt.Println("----邮件内容------", buff.String())
	//发送邮件
	err1 := email.SendEmailByGomail(subject, recipient.Email, buff.String())
	if err1 != nil {
		zap.L().Error("邮件发送失败", zap.Error(err1))
		return
	}
	//解锁
	lock.Unlock()
	zap.L().Info("发送邮件结束---" + recipient.Email)
	zap.L().Info(recipient.Name + "<" + recipient.Email + ">" + "的邮件发送成功")

}
